package work.xiaohong.mina.service.impl;


import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.springframework.stereotype.Service;
import work.xiaohong.mina.handler.plc.ClientHandler;
import work.xiaohong.mina.handler.plc.ServerHandler;
import work.xiaohong.mina.message.GeneralMessage;
import work.xiaohong.mina.model.Result;
import work.xiaohong.mina.protocol.PlcProtocolDecoder;
import work.xiaohong.mina.protocol.PlcProtocolEncoder;
import work.xiaohong.mina.service.SocketService;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author Kevin
 * @data 3/17/2022 6:13 PM
 * @description Service层实现类
 **/
@Slf4j
@Service
public class SocketServiceImpl implements SocketService {

  // 使用线程安全的map储存客户端服务和服务端服务
  // plc客户端信息储存
  private ConcurrentHashMap<ConcurrentHashMap<String,Integer>, IoSession> plcClientMap = new ConcurrentHashMap();
  // plc服务端信息储存
  private ConcurrentHashMap<Integer, IoAcceptor> plcServertMap = new ConcurrentHashMap();
//  // plc服务端会话储存
//  private ConcurrentHashMap<Integer, Set<Channel>> plcServerConnectMap = new ConcurrentHashMap();

  // 保证原子性
  private volatile boolean clientConnectionState = false;

  // 判断失联 (保证原子性)
  private volatile Integer failTimes = 0; // 失败次数统计，超过三次则判断服务端失联

  // 消息序列号 (保证原子性)
  private volatile Integer sequence = 0;

  /**
   * 创建PLC客户端
   * @param ip 服务端ip
   * @param port 服务端端口
   * @return 返回创建结果
   */
  public Result plcClientCreat(String ip, Integer port){

    ConcurrentHashMap<String,Integer> mapKey = new ConcurrentHashMap<String,Integer>();
    mapKey.put(ip,port);
    if (plcClientMap.containsKey(mapKey)){
      return Result.ERROR(ip+":"+port+"的PLC客户端已存在!");
    }

    IoConnector connector = new NioSocketConnector();
    connector.getFilterChain().addLast("coderc",new ProtocolCodecFilter(new ProtocolCodecFactory() {
      @Override
      public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception {
        return new PlcProtocolEncoder();
      }

      @Override
      public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
        return new PlcProtocolDecoder();
      }
    }));

    connector.getSessionConfig().setReadBufferSize(1024);
    // 6s没有写操作自动触发心跳包 维持连接
    connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE,6);
    connector.setHandler(new ClientHandler());

    ConnectFuture connectFuture = connector.connect(new InetSocketAddress(ip, port));

    connectFuture.addListener(new IoFutureListener<IoFuture>() {
      @Override
      public void operationComplete(IoFuture ioFuture) {
        if (connectFuture.isConnected()){
          IoSession session = connectFuture.getSession();
          // 保存当前连接信息
          plcClientMap.put(mapKey,session);
        }
      }
    });


    return Result.OK(ip+":"+port+"的PLC客户端启动成功!");

  }


  /**
   * 创建PLC服务端
   * @param port 服务端端口
   * @return 返回创建结果
   */
  public Result plcServerCreat(Integer port){

    if (plcServertMap.containsKey(port)){
      return Result.ERROR("端口为"+port+"的PLC服务端已存在!");
    }

    IoAcceptor acceptor = new NioSocketAcceptor();
    acceptor.getFilterChain().addLast("coderc",new ProtocolCodecFilter(new ProtocolCodecFactory() {
      @Override
      public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception {
        return new PlcProtocolEncoder();
      }

      @Override
      public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
        return new PlcProtocolDecoder();
      }
    }));

    acceptor.getSessionConfig().setReadBufferSize(1024);
    // 18s没有读操作了，关闭连接
    acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE,18);
    acceptor.setHandler(new ServerHandler());
    try {
      acceptor.bind(new InetSocketAddress(port));
      plcServertMap.put(port,acceptor);
      return Result.OK("端口为"+port+"的PLC服务端启动成功!");
    }
    catch (IOException e) {
      e.printStackTrace();
      return Result.ERROR("端口为"+port+"的PLC服务端启动失败!");
    }



  }


  /**
   * 指定PLC客户端发送消息
   * @param ip 服务端ip
   * @param port 服务端端口
   * @param message 要发送的消息
   * @return 返回创建结果
   */
  public Result sendByPlcClient(String ip, Integer port, GeneralMessage message){

    ConcurrentHashMap<String,Integer> mapKey = new ConcurrentHashMap<String,Integer>();
    mapKey.put(ip,port);
    if (!plcClientMap.containsKey(mapKey)){
      return Result.ERROR(ip+":"+port+"的PLC客户端不存在!");
    }

    IoSession ioSession = plcClientMap.get(mapKey);

    ioSession.write(message);

    return Result.ERROR(ip+":"+port+"的PLC客户端发送消息【"+message+"】成功!");
  }


//  /**
//   * 指定PLC服务端发送消息
//   * @param port 服务端端口
//   * @param message 要发送的消息
//   * @return 返回创建结果
//   */
//  public Result sendByPlcServer(Integer port, GeneralMessage message){
//
//    if (!plcServerConnectMap.containsKey(port)){
//      return Result.ERROR("端口为"+port+"的PLC服务端不存在!");
//    }
//
//    // 给所有的连接都发送消息
//    for (Channel channel : plcServerConnectMap.get(port)) {
//      channel.writeAndFlush(message);
//    }
//
//
//    return Result.ERROR("端口为"+port+"的PLC服务端发送消息【"+message+"】成功!");
//
//  }

  /**
   * 关闭指定PLC客户端
   * @param ip 服务端ip
   * @param port 服务端端口
   * @return 返回创建结果
   */
  public Result plcClientClose(String ip, Integer port){
    ConcurrentHashMap<String,Integer> mapKey = new ConcurrentHashMap<String,Integer>();
    mapKey.put(ip,port);
    if (!plcClientMap.containsKey(mapKey)){
      return Result.ERROR(ip+":"+port+"的PLC客户端不存在!");
    }

    IoSession ioSession = plcClientMap.get(mapKey);

    ioSession.closeNow();

    return Result.OK(ip+":"+port+"的PLC客户端关闭成功!");

  }

//  /**
//   * 关闭指定PLC服务端
//   * @param port 服务端端口
//   * @return 返回创建结果
//   */
//  public Result plcServerClose(Integer port){
//    if (!plcServerConnectMap.containsKey(port)){
//      return Result.ERROR("端口为"+port+"的PLC服务端不存在!");
//    }
//
//    Channel channel = plcServertMap.get(port);
//
//    channel.close();
//
//    return Result.OK("端口为"+port+"的PLC服务端关闭成功!");
//
//  }


}
